#include "bsp_w25q64.h"

void spi_port_init(void) {        
    spi_parameter_struct spi_init_struct;

    rcu_periph_clock_enable(RCU_GPIOF);
    rcu_periph_clock_enable(RCU_SPI4);

    gpio_af_set(GPIOF, GPIO_AF_5, GPIO_PIN_7);
    gpio_af_set(GPIOF, GPIO_AF_5, GPIO_PIN_8);
    gpio_af_set(GPIOF, GPIO_AF_5, GPIO_PIN_9);
    gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
    gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_8);
    gpio_mode_set(GPIOF, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_9);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_7);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);

    rcu_periph_clock_enable(RCU_GPIOF);
    gpio_mode_set(GPIOF, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_6);
    gpio_output_options_set(GPIOF, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_6);
    gpio_bit_write(GPIOF, GPIO_PIN_6, SET);

    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX; 
    spi_init_struct.device_mode          = SPI_MASTER; 
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_HIGH_PH_2EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_2;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;
    spi_init(SPI4, &spi_init_struct);
    spi_enable(SPI4);
}

uint8_t spi_read_write_byte(uint8_t dat) {
    while(RESET == spi_i2s_flag_get(SPI4,  SPI_FLAG_TBE) );
    spi_i2s_data_transmit(SPI4, dat);
    while(RESET == spi_i2s_flag_get(SPI4,  SPI_FLAG_RBNE) );
    return spi_i2s_data_receive(SPI4);
}

uint16_t W25Q64_readID(void) {
    gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);
    spi_read_write_byte(0x90);
    spi_read_write_byte(0x00);
    spi_read_write_byte(0x00);
    spi_read_write_byte(0x00);
    
    uint16_t temp = 0;
    temp |= spi_read_write_byte(0xFF) << 8;
    temp |= spi_read_write_byte(0xFF);
    gpio_bit_write(GPIOF, GPIO_PIN_6, SET);
    return temp;
}

void W25Q64_write_enable(void){
    gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);                           
    spi_read_write_byte(0x06);                  
    gpio_bit_write(GPIOF, GPIO_PIN_6, SET); 
}                                          


void W25Q64_wait_busy(void){   
    unsigned char byte = 0;
    
    do { 
        gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);                              
        spi_read_write_byte(0x05);                 
        byte = spi_read_write_byte(0Xff);       
        gpio_bit_write(GPIOF, GPIO_PIN_6, SET);         
    }while( ( byte & 0x01 ) == 1 );  
}  

void W25Q64_erase_chip (void) {
    W25Q64_write_enable();
    W25Q64_wait_busy();

    gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);                                        
    spi_read_write_byte(0x60);                
    gpio_bit_write(GPIOF, GPIO_PIN_6, SET);                  

    W25Q64_wait_busy();       
}

void W25Q64_write_sector(uint8_t *buffer, uint32_t addr, uint16_t bytes) {
    int page_cnt = bytes / 256;
    while (page_cnt--) {
        W25Q64_write_enable();
        W25Q64_wait_busy();    

        gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);                                        
        spi_read_write_byte(0x02);                    
        spi_read_write_byte((uint8_t)((addr)>>16));     
        spi_read_write_byte((uint8_t)((addr)>>8));   
        spi_read_write_byte((uint8_t)addr);   
        for(int i=0;i<256;i++) {
                spi_read_write_byte(buffer[i]);  
        }
        gpio_bit_write(GPIOF, GPIO_PIN_6, SET);
        W25Q64_wait_busy(); 

        addr += 256;
        buffer += 256;
    }         
}

void W25Q64_read_sector(uint8_t *buffer, uint32_t addr, uint16_t bytes) {
    uint16_t i;                   
    gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);            
    spi_read_write_byte(0x03);                           
    spi_read_write_byte((uint8_t)((addr)>>16));           
    spi_read_write_byte((uint8_t)((addr)>>8));   
    spi_read_write_byte((uint8_t)addr);   
    for(i=0;i<bytes;i++) { 
        buffer[i]= spi_read_write_byte(0XFF);  
    }
    gpio_bit_write(GPIOF, GPIO_PIN_6, SET);      
}

void W25Q64_erase_sector (uint32_t addr) {
    W25Q64_write_enable();
    W25Q64_wait_busy();

    gpio_bit_write(GPIOF, GPIO_PIN_6, RESET);                                        
    spi_read_write_byte(0x20);                
    spi_read_write_byte((uint8_t)((addr)>>16));      
    spi_read_write_byte((uint8_t)((addr)>>8));   
    spi_read_write_byte((uint8_t)addr);  
    gpio_bit_write(GPIOF, GPIO_PIN_6, SET);                  

    W25Q64_wait_busy(); 
}

